package io.github.yedaxia.apidocs;

import com.github.javaparser.ast.body.ClassOrInterfaceDeclaration;
import io.github.yedaxia.apidocs.exception.ConfigException;
import io.github.yedaxia.apidocs.parser.AbsControllerParser;
import io.github.yedaxia.apidocs.parser.ControllerNode;
import io.github.yedaxia.apidocs.parser.SpringControllerParser;

import java.io.File;
import java.util.*;

/**
 * to judge project which framework is using and make some initialization
 *
 * @author yeguozhong yedaxia.github.com
 */
public class DocContext {
    private static String docPath;
    /**
     * multi modules
     */
    private static final List<String> JAVA_SRC_PATHS = new ArrayList<>();
    private static AbsControllerParser controllerParser;
    private static final List<File> CONTROLLER_FILES = new ArrayList<>();
    private static IResponseWrapper responseWrapper;
    private static DocsConfig config;
    private static I18n i18n;
    private static String currentApiVersion;
    private static final List<String> API_VERSION_LIST = new ArrayList<>();
    private static List<ControllerNode> lastVersionControllerNodes;
    private static List<ControllerNode> controllerNodeList;

    public static void init(DocsConfig config) {

        if (config.getApiVersion() == null) {
            throw new ConfigException("api version cannot be null");
        }

        if (config.getProjectName() == null) {
            config.setProjectName("JApiDocs");
        }

        DocContext.config = config;
        i18n = new I18n(config.getLocale());
        DocContext.currentApiVersion = config.getApiVersion();
        setDocPath(config);
        initApiVersions();

        File logFile = getLogFile();
        if (logFile.exists()) {
            LogUtils.info("delete log file : %s", logFile.delete());
        }

        JAVA_SRC_PATHS.addAll(config.getJavaSrcPaths());
        findOutControllers();
        initLastVersionControllerNodes();
    }

    private static void initLastVersionControllerNodes() {
        File docDir = new File(docPath).getParentFile();
        File[] childDirs = docDir.listFiles();
        if (childDirs != null && childDirs.length != 0) {
            File lastVerDocDir = null;
            for (File childDir : childDirs) {
                if (childDir.isDirectory() && !currentApiVersion.equals(childDir.getName())
                        && (lastVerDocDir == null || childDir.lastModified() > lastVerDocDir.lastModified())) {
                    lastVerDocDir = childDir;
                }
            }
            if (lastVerDocDir != null) {
                lastVersionControllerNodes = CacheUtils.getControllerNodes(lastVerDocDir.getName());
            }
        }
    }

    /**
     * 获取文档目录下所有api版本
     */
    private static void initApiVersions() {
        File docDir = new File(docPath).getParentFile();
        String[] diffVersionApiDirs = docDir.list((dir, name) -> dir.isDirectory() && !name.startsWith("."));
        if (diffVersionApiDirs != null) {
            Collections.addAll(DocContext.API_VERSION_LIST, diffVersionApiDirs);
        }
    }

    private static void findOutControllers() {
        // 获取 controller
        for (String javaSrcPath : JAVA_SRC_PATHS) {
            LogUtils.info("start find controllers in path : %s", javaSrcPath);
            File javaSrcDir = new File(javaSrcPath);
            List<File> result = new ArrayList<>();
            controllerParser = new SpringControllerParser();
            Utils.wideSearchFile(javaSrcDir, (f, name) -> f.getName().endsWith(".java") && ParseUtils.compilationUnit(f)
                            .findAll(ClassOrInterfaceDeclaration.class)
                            .stream()
                            .anyMatch(cd -> (cd.getAnnotationByName("Controller").isPresent()
                                    || cd.getAnnotationByName("RestController").isPresent())
                                    && cd.getAnnotationByName(Ignore.class.getSimpleName()).isEmpty())
                    , result, false);
            CONTROLLER_FILES.addAll(result);
            for (File controllerFile : result) {
                LogUtils.info("find controller file : %s", controllerFile.getName());
            }
        }
    }

    /**
     * get log file path
     *
     * @return current log
     */
    public static File getLogFile() {
        return new File(DocContext.getDocPath(), "apiDoc.log");
    }

    /**
     * api docs output path
     *
     * @return
     */
    public static String getDocPath() {
        return docPath;
    }

    private static void setDocPath(DocsConfig config) {
        if (config.docsPath == null || config.docsPath.isEmpty()) {
            config.docsPath = System.getProperty("user.dir");
        }
        File docDir = new File(config.docsPath, config.apiVersion);
        if (!docDir.exists()) {
            LogUtils.info("create Doc File: ", docDir.mkdirs());
        }
        DocContext.docPath = docDir.getAbsolutePath();
    }

    /**
     * get all controllers in this project
     *
     * @return
     */
    public static File[] getControllerFiles() {
        return CONTROLLER_FILES.toArray(new File[0]);
    }

    /**
     * get controller parser, it will return different parser by different framework you are using.
     *
     * @return
     */
    public static AbsControllerParser controllerParser() {
        return controllerParser;
    }

    public static IResponseWrapper getResponseWrapper() {
        if (responseWrapper == null) {
            responseWrapper = responseNode -> {
                Map<String, Object> resultMap = new HashMap<>();
                resultMap.put("code", 0);
                resultMap.put("data", responseNode);
                resultMap.put("msg", "success");
                return resultMap;
            };
        }
        return responseWrapper;
    }

    public static List<ControllerNode> getControllerNodeList() {
        return controllerNodeList;
    }

    static void setControllerNodeList(List<ControllerNode> controllerNodeList) {
        DocContext.controllerNodeList = controllerNodeList;
    }

    public static DocsConfig getDocsConfig() {
        return DocContext.config;
    }

    public static String getCurrentApiVersion() {
        return currentApiVersion;
    }

    public static List<String> getApiVersionList() {
        return API_VERSION_LIST;
    }

    public static List<ControllerNode> getLastVersionControllerNodes() {
        return lastVersionControllerNodes;
    }

    public static I18n getI18n() {
        return i18n;
    }

    static void setResponseWrapper(IResponseWrapper responseWrapper) {
        DocContext.responseWrapper = responseWrapper;
    }

    public static List<String> getJavaSrcPaths() {
        return JAVA_SRC_PATHS;
    }
}
